package com.googlecode.lookie.listview;

import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;

import javax.imageio.ImageIO;
import javax.swing.DefaultListModel;
import javax.swing.JComponent;
import javax.swing.JList;
import javax.swing.JScrollPane;
import javax.swing.ListModel;
import javax.swing.SwingUtilities;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;

import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.richclient.application.support.AbstractView;
import org.springframework.richclient.settings.Settings;

import com.googlecode.lookie.explorer.DirectorySelectionEvent;
import com.googlecode.lookie.imagedb.ImageDao;
import com.googlecode.lookie.imagedb.ImageData;
import com.googlecode.lookie.platform.image.ImageUtils;
import com.googlecode.lookie.platform.image.format.Format;
import com.googlecode.lookie.platform.image.format.FormatResolver;
import com.googlecode.lookie.platform.image.io.Reader;
import com.googlecode.lookie.platform.image.io.ReaderRegistry;
import com.googlecode.lookie.platform.image.resource.FileResource;
import com.googlecode.lookie.platform.settings.LookieSettingsManager;
import com.googlecode.lookie.platform.settings.SettingsIds;
import com.googlecode.lookie.swing.iconlist.CellRenderer;
import com.googlecode.lookie.swing.iconlist.DelayedLabelProvider;

public class ImageListView
    extends AbstractView
    implements ListSelectionListener, ApplicationListener, PropertyChangeListener
{

    private JScrollPane scrollPane;

    private JList list;

    private IconProvider iconProvider;

    private CellRenderer renderer;

    public LookieSettingsManager getSettingsManager()
    {
        return (LookieSettingsManager) getApplicationServices().getService( LookieSettingsManager.class );
    }

    public ImageDao getImageDao()
    {
        return (ImageDao) getApplicationServices().getService( ImageDao.class );
    }

    public FormatResolver getFormatResolver()
    {
        return (FormatResolver) getApplicationServices().getService( FormatResolver.class );
    }

    public ReaderRegistry getReaderRegistry()
    {
        return (ReaderRegistry) getApplicationServices().getService( ReaderRegistry.class );
    }

    @Override
    protected JComponent createControl()
    {
        list = new JList();

        iconProvider = new IconProvider( list );

        renderer = new CellRenderer( iconProvider );

        Settings userSettings = getSettingsManager().getUserSettings();
        int thumbSize = userSettings.getInt( SettingsIds.THUMB_SIZE );

        userSettings.addPropertyChangeListener( SettingsIds.THUMB_SIZE, this );

        renderer.setIconAreaSize( thumbSize );

        list.setCellRenderer( renderer );

        list.setFixedCellHeight( renderer.getComponentSize().height );
        list.setFixedCellWidth( renderer.getComponentSize().width );
        list.setVisibleRowCount( -1 );
        list.setLayoutOrientation( JList.HORIZONTAL_WRAP );

        list.addListSelectionListener( this );

        scrollPane = new JScrollPane( list );
        scrollPane.setAutoscrolls( true );

        return scrollPane;
    }

    public void onApplicationEvent( ApplicationEvent event )
    {
        if ( event instanceof DirectorySelectionEvent )
        {
            iconProvider.cancel();
            list.setModel( createListModel( ( (DirectorySelectionEvent) event ).getFile() ) );
        }
    }

    private ListModel createListModel( File dir )
    {
        DefaultListModel model = new DefaultListModel();

        // should we use FileSystemView.getFiles ?
        File[] files = dir.listFiles( new ImageFileFilter() );
        if ( files != null )
        {
            for ( File imageFile : files )
            {
                if ( imageFile != null )
                {
                    model.addElement( imageFile );
                }
            }
        }

        return model;
    }

    private class ImageFileFilter
        implements FileFilter
    {

        public boolean accept( File pathname )
        {
            Format format = getFormatResolver().getFormatByName( pathname.getName() );
            Reader reader = getReaderRegistry().getReader( format );
            return reader != null;
        }

    }

    private class IconProvider
        extends DelayedLabelProvider
    {

        public IconProvider( JList list )
        {
            super( list );
        }

        @Override
        protected BufferedImage getImage( Object value )
        {
            byte[] bs = getImageDao().getImage( ( (File) value ).getAbsolutePath() );
            BufferedImage image = ImageUtils.byteArrayToBufferedImage( bs );
            return image;
        }

        @Override
        protected boolean isImageReady( Object value )
        {
            File file = (File) value;
            ImageData data = getImageDao().getImageData( file.getAbsolutePath() );

            if ( data != null && data.getLastModified() != file.lastModified() )
            {
                // TODO rework to allow for displaying the cached version of an image while the recent one is
                // created and saved to the database
                data = null;
            }

            return data != null;
        }

        @Override
        protected void prepareImage( Object value )
        {
            Image image = null;

            Format format = getFormatResolver().getFormatByName( ( (File) value ).getAbsolutePath() );
            Reader reader = getReaderRegistry().getReader( format );

            try
            {
                image = reader.read( new FileResource( (File) value ) );
            }
            catch ( Exception e )
            {
                // TODO
                e.printStackTrace();
            }

            ByteArrayOutputStream baos = null;

            if ( image != null )
            {
                baos = new ByteArrayOutputStream();

                BufferedImage bu = null;

                if ( image instanceof BufferedImage )
                {
                    bu = (BufferedImage) image;
                }
                else
                {
                    bu = new BufferedImage( image.getWidth( null ), image.getHeight( null ), BufferedImage.TYPE_INT_RGB );
                    Graphics2D g2 = bu.createGraphics();
                    g2.drawImage( image, 0, 0, null );
                    g2.dispose();
                }

                if ( bu.getWidth() > 300 || bu.getHeight() > 300 )
                {
                    bu = ImageUtils.scaleIcon( bu, 300 );
                }

                try
                {
                    ImageIO.write( bu, "JPEG", baos );
                }
                catch ( IOException e )
                {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }

            File file = (File) value;

            ImageData data = getImageDao().getImageData( file.getAbsolutePath() );

            if ( data == null )
            {
                data = new ImageData();
                data.setId( file.getAbsolutePath() );
                data.setLastModified( file.lastModified() );
                getImageDao().insert( data, baos == null ? null : baos.toByteArray() );
            }
            else
            {
                data.setLastModified( file.lastModified() );
                getImageDao().update( data, baos == null ? null : baos.toByteArray() );
            }
        }

        public String getText( Object value )
        {
            return ( (File) value ).getAbsolutePath();
        }

        public String getTooltipText( Object value )
        {
            String tip = "<html><table><tr><td><b>Filename:</b></td><td>%s</td></tr><tr><td><b>Dimensions:</b></td><td>s</td></tr></html>";
            return String.format( tip, ( (File) value ).getAbsolutePath() );
        }

    }

    public void valueChanged( ListSelectionEvent e )
    {
        if ( list.isSelectionEmpty() )
        {
            getApplicationContext().publishEvent( new ImageSelectionEvent( this ) );
        }
        else
        {
            File file = (File) list.getModel().getElementAt( list.getSelectedIndex() );
            getApplicationContext().publishEvent( new ImageSelectionEvent( this, file ) );
        }
    }

    public void propertyChange( final PropertyChangeEvent evt )
    {
        SwingUtilities.invokeLater( new Runnable()
        {

            public void run()
            {
                Integer size = (Integer) evt.getNewValue();
                renderer.setIconAreaSize( size );

                list.setFixedCellHeight( renderer.getComponentSize().height );
                list.setFixedCellWidth( renderer.getComponentSize().width );

                list.repaint();
            }

        } );
    }

}
